package mtask

import (
	"gitee.com/dennis-mxx/gcode-common/mcontext"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"
)

type Worker interface {
	Run(chainId int)
}
type WorkerChain struct {
	num       int
	chain     chan func(chainId int)
	chainStat []int
	wg        sync.WaitGroup
	stat      bool
	catch     func(err any)
}

func NewWorker(mtx *mcontext.MContext, num int, catch func(err any)) *WorkerChain {
	logger := mtx.Logger()
	if catch == nil {
		catch = func(err any) {
			logger.Error("WorkerChain DoRunner fail >>> ", err)
		}
	}
	workChain := &WorkerChain{
		num:       num,
		chainStat: make([]int, num),
		chain:     make(chan func(chainId int), 0),
		wg:        sync.WaitGroup{},
		stat:      false,
		catch:     catch,
	}
	workChain.Start()
	return workChain
}
func (ce *WorkerChain) Start() {
	ce.stat = true
	for i := 0; i < ce.num; i++ {
		ce.start(i)
	}
}
func (ce *WorkerChain) start(index int) {
	go func() {
		for {
			if !ce.stat {
				break
			}
			ce.doRunner(index)
		}
	}()
}
func (ce *WorkerChain) doRunner(index int) {
	defer func() {
		if err := recover(); err != nil {
			ce.catch(err)
		}
	}()
	fn := <-ce.chain
	if fn != nil {
		ce.chainStat[index] = 1
		defer func() {
			ce.chainStat[index] = 0
		}()
		fn(index)
	}
}
func (ce *WorkerChain) Stop() {
	ce.stat = false
	if ce.catch != nil {
		close(ce.chain)
	}
}

func (ce *WorkerChain) Submit(fn func(chainId int)) {
	go func() {
		ce.chain <- fn
	}()
}
func (ce *WorkerChain) SubmitWorker(fn Worker) {
	go func() {
		ce.chain <- fn.Run
	}()
}
func (ce *WorkerChain) Await() {
	for {
		if ce.IsEmpty() {
			time.Sleep(500 * time.Millisecond)
			if ce.IsEmpty() {
				time.Sleep(500 * time.Millisecond)
				if ce.IsEmpty() {
					time.Sleep(500 * time.Millisecond)
					if ce.IsEmpty() {
						return
					}
				}
			}
		}
		time.Sleep(3 * time.Second)

	}
}
func (ce *WorkerChain) IsEmpty() bool {
	empty := true
	for i := range ce.chainStat {
		if ce.chainStat[i] == 1 {
			empty = false
			break
		}
	}
	return empty
}
func (ce *WorkerChain) Block() {
	sig := make(chan os.Signal, 2)
	signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT)
	<-sig
}
